package com.googlecode.economini.issues;

import java.util.List;

import android.content.ContentProvider;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.UriMatcher;
import android.database.Cursor;
import android.database.CursorWrapper;
import android.database.sqlite.SQLiteDatabase;
import android.net.Uri;
import android.text.TextUtils;

public class IssueProvider extends ContentProvider {

  private static final String AUTHORITY = "com.googlecode.economini";

  public static final Uri ISSUE_URI = Uri.parse("content://" + AUTHORITY
      + "/issue");

  public static final String ISSUE_MIME = "vnd.android.cursor.item/vnd.economini.issue";
  public static final String ISSUE_DIR_MIME = "vnd.android.cursor.dir/vnd.economini.issue";
  public static final String ARTICLE_MIME = "vnd.android.cursor.item/vnd.economini.article";
  public static final String ARTICLE_DIR_MIME = "vnd.android.cursor.dir/vnd.economini.article";

  private static final int ISSUE_ITEM = 0;
  private static final int ISSUE_DIR = 1;
  private static final int ARTICLE_ITEM = 2;
  private static final int ARTICLE_DIR = 3;

  private static final UriMatcher URI_MATCHER;
  static {
    URI_MATCHER = new UriMatcher(UriMatcher.NO_MATCH);
    URI_MATCHER.addURI(AUTHORITY, "issue", ISSUE_DIR);
    URI_MATCHER.addURI(AUTHORITY, "issue/#", ISSUE_ITEM);
    URI_MATCHER.addURI(AUTHORITY, "issue/#/article", ARTICLE_DIR);
    URI_MATCHER.addURI(AUTHORITY, "issue/#/article/#", ARTICLE_ITEM);
  }

  private IssueStore issueSummaryStore;

  @Override
  public boolean onCreate() {
    issueSummaryStore = new IssueStore(getContext());
    return true;
  }

  @Override
  public String getType(Uri uri) {
    switch (URI_MATCHER.match(uri)) {
      case ISSUE_ITEM:
        return ISSUE_MIME;
      case ISSUE_DIR:
        return ISSUE_DIR_MIME;
      case ARTICLE_ITEM:
        return ARTICLE_MIME;
      case ARTICLE_DIR:
        return ARTICLE_DIR_MIME;
      default:
        return null;
    }
  }

  @Override
  public int delete(Uri uri, String selection, String[] selectionArgs) {
    int uriMatch = URI_MATCHER.match(uri);
    selection = buildSelection(uriMatch, uri, selection);
    String tableName = getTableName(uriMatch);

    SQLiteDatabase db = issueSummaryStore.getWritableDatabase();
    try {
      int deleteCount = db.delete(
          tableName,
          selection,
          selectionArgs);
      getContext().getContentResolver().notifyChange(uri, null);
      return deleteCount;
    } finally {
      db.close();
    }
  }

  @Override
  public Uri insert(Uri uri, ContentValues values) {
    int uriMatch = URI_MATCHER.match(uri);
    switch(uriMatch) {

      case ISSUE_ITEM:
      case ARTICLE_ITEM:
        throw new IllegalArgumentException("Id cannot be specified for insert");

      case ISSUE_DIR:
      case ARTICLE_DIR:
        // Do nothing
        break;

      default:
        throw new IllegalArgumentException("URI " + uri.toString()
            + " is invalid");
    }

    String tableName = getTableName(uriMatch);

    SQLiteDatabase db = issueSummaryStore.getWritableDatabase();

    try {
      long id = db.insertWithOnConflict(
          tableName,
          null,
          values,
          SQLiteDatabase.CONFLICT_REPLACE);

      // TODO call notifyChange
      Uri insertedUri = ContentUris.withAppendedId(ISSUE_URI, id);
      getContext().getContentResolver().notifyChange(insertedUri, null);
      return insertedUri;
    } finally {
      db.close();
    }
  }

  @Override
  public int update(Uri uri, ContentValues values, String selection,
      String[] selectionArgs) {
    int uriMatch = URI_MATCHER.match(uri);
    selection = buildSelection(uriMatch, uri, selection);
    String tableName = getTableName(uriMatch);

    SQLiteDatabase db = issueSummaryStore.getWritableDatabase();

    try {
      int updateCount = db.updateWithOnConflict(
          tableName,
          values,
          selection,
          selectionArgs,
          SQLiteDatabase.CONFLICT_REPLACE);

      // TODO call notifyChange
      getContext().getContentResolver().notifyChange(uri, null);
      return updateCount;
    } finally {
      db.close();
    }
  }

  @Override
  public Cursor query(Uri uri, String[] projection, String selection,
      String[] selectionArgs, String sortOrder) {
    int uriMatch = URI_MATCHER.match(uri);
    selection = buildSelection(uriMatch, uri, selection);
    String tableName = getTableName(uriMatch);

    SQLiteDatabase db = issueSummaryStore.getWritableDatabase();
    Cursor cursor = db.query(tableName, projection, selection,
        selectionArgs, null, null, sortOrder);

    cursor.setNotificationUri(getContext().getContentResolver(), uri);

    return new DbClosingCursor(cursor, db);
  }

  /**
   * Build a selection string given a URI and existing selection string.
   * 
   * @param uriMatch the value returned by {@link #URI_MATCHER} for the
   *        supplied {@code Uri}
   * @param uri the {@code Uri}
   * @param selection the selection string
   * 
   * @return a new, possibly empty selection string
   * @throws IllegalArgumentException if {@code uriMatch} does not match an
   *         expected URI format
   */
  private static String buildSelection(
      int uriMatch,
      Uri uri,
      String selection) {
    if (uriMatch == UriMatcher.NO_MATCH) {
      throw new IllegalArgumentException("Unexpected URI");
    }

    StringBuilder sb = new StringBuilder();
    // Initialize the string builder with the selection, if any
    if (!TextUtils.isEmpty(selection)) {
      sb.append('(').append(selection).append(')');
    }

    // Check if either item case matches, and add the ID condition
    switch (uriMatch) {
      case ISSUE_ITEM:
      case ARTICLE_ITEM:
        if (sb.length() != 0) {
          sb.append(" and ");
        }
        sb.append(IssueStore.ID_COLUMN)
            .append("=")
            .append(ContentUris.parseId(uri));
        break;
    }

    // Check if either article case matches, and add the article issue ID
    // condition
    switch (uriMatch) {
      case ARTICLE_DIR:
      case ARTICLE_ITEM:
        List<String> uriSegments = uri.getPathSegments();
        long issueId = Long.parseLong(uriSegments.get(1));

        if (sb.length() != 0) {
          sb.append(" and ");
        }
        sb.append(IssueStore.ARTICLE_ISSUE_ID_COLUMN)
            .append("=")
            .append(issueId);
    }

    return sb.toString();
  }

  private static String getTableName(int uriMatch) {
    switch (uriMatch) {
      case ISSUE_ITEM:
      case ISSUE_DIR:
        return IssueStore.ISSUE_TABLE;

      case ARTICLE_ITEM:
      case ARTICLE_DIR:
        return IssueStore.ARTICLE_TABLE;

      default:
        return null;
    }
  }

  private static class DbClosingCursor extends CursorWrapper {

    private final SQLiteDatabase db;

    public DbClosingCursor(Cursor cursor, SQLiteDatabase db) {
      super(cursor);
      this.db = db;
    }

    @Override
    public void close() {
      super.close();
      db.close();
    }
  }
}
